﻿using AutoMapper;
using IOA.MES.Common;
using IOA.MES.DataAccess.Entity;
using IOA.MES.DataAccess.Enum;
using IOA.MES.DataAccess.VModel;
using IOA.Utils;
using NLog;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;

namespace IOA.MES.Business
{
    /// <summary>
    /// 调拨管理（错误代码：133001）
    /// </summary>
    public class AllocateOrderMgr
    {
        private MESContext mesDb = new MESContext("MES");
        private Logger logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 查询调拨订单列表
        /// </summary>
        /// <param name="pageIndex">页数</param>
        /// <param name="pageSize">条目</param>
        /// <param name="total">总共</param>
        /// <param name="toWarehouseCode">收获仓库代码</param>
        /// <param name="workOrderNo">工单Id</param>
        /// <param name="allocateNo">调拨订单号</param>
        /// <returns></returns>
        public List<VAllocateOrder> QueryAllocateOrders(int pageIndex, int pageSize, out int total, string toWarehouseCode, string workOrderNo, string allocateNo,
            bool? reviewed, DateTime? startTime = null, DateTime? endTime = null)
        {
            var query = mesDb.AllocateOrders.AsNoTracking().Where(_ => _.Valid);
            if (!string.IsNullOrWhiteSpace(allocateNo))
            {
                query = query.Where(_ => _.AllocateNo == allocateNo);
            }
            else if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                query = query.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            else
            {
                if (!string.IsNullOrEmpty(toWarehouseCode))
                {
                    query = query.Where(_ => _.ToWarehouseCode == toWarehouseCode);
                }
                if (startTime.HasValue)
                {
                    query = query.Where(_ => _.CreateTime >= startTime.Value);
                }
                if (endTime.HasValue)
                {
                    query = query.Where(_ => _.CreateTime < endTime.Value);
                }
                if (reviewed == true)
                {
                    query = query.Where(_ => _.ReviewTime.HasValue);
                }
                else if (reviewed == false)
                {
                    query = query.Where(_ => !_.ReviewTime.HasValue);
                }
            }

            total = query.Count();
            query = query.OrderByDescending(_ => _.PkId).ThenBy(_ => _.AllocateNo);

            var entities = pageSize <= 0 ? query.ToList() : query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            var models = Mapper.Map<List<VAllocateOrder>>(entities);
            return models;
        }

        /// <summary>
        /// 获取调拨单对象
        /// </summary>
        /// <param name="allocateNo">调拨单号</param>
        /// <returns></returns>
        public VAllocateOrder GetAllocateOrder(string allocateNo)
        {
            var entity = mesDb.AllocateOrders.AsNoTracking().FirstOrDefault(_ => _.AllocateNo == allocateNo && _.Valid);
            if (entity == null)
            {
                return null;
            }
            var model = Mapper.Map<VAllocateOrder>(entity);
            model.Products = Mapper.Map<List<VAllocateProduct>>(mesDb.AllocateProducts.AsNoTracking().Where(_ => _.AllocateNo == model.AllocateNo && _.Valid).ToList());
            return model;
        }

        /// <summary>
        /// 同步WMS搬运单
        /// </summary>
        public void AsyncMoveOrder(List<VAllocateOrder> models)
        {
            models = models.Where(_ => !string.IsNullOrWhiteSpace(_.AllocateNo) && !string.IsNullOrWhiteSpace(_.WorkOrderNo) && _.Products.Any()).ToList();
            if (!models.Any())
            {
                return;
            }

            var categories = mesDb.ProductCategories.AsNoTracking().Where(_ => _.Valid).ToList();
            var allocateNos = models.Select(_ => _.AllocateNo.ToUpper().Trim()).Distinct().ToList();
            var allocateOrders = mesDb.AllocateOrders.AsNoTracking().Where(_ => allocateNos.Contains(_.AllocateNo) && _.Valid).ToList();
            var allocateProducts = mesDb.AllocateProducts.AsNoTracking().Where(_ => allocateNos.Contains(_.AllocateNo) && _.Valid).ToList();
            foreach (VAllocateOrder model in models)
            {
                var workOrder = mesDb.WorkOrders.AsNoTracking().FirstOrDefault(_ => _.WorkOrderNo == model.WorkOrderNo && _.Valid);
                var curAllocate = allocateOrders.FirstOrDefault(_ => StringUtils.Equals(model.AllocateNo, _.AllocateNo));
                var curAllocateProducts = allocateProducts.Where(_ => StringUtils.Equals(model.AllocateNo, _.AllocateNo)).ToList();

                if (curAllocate == null)
                {
                    curAllocate = new AllocateOrder
                    {
                        OrderNo = workOrder?.OrderNo,
                        WorkOrderNo = model.WorkOrderNo,
                        AllocateNo = model.AllocateNo,
                        AllocateTime = DateTime.Now,
                        BatchAsyncFinished = true,
                        BatchLastAsyncBy = 0,
                        BatchLastAsyncTime = DateTime.Now,
                        ReviewBy = 0,
                        ReviewTime = DateTime.Now,
                        CreateBy = 0,
                        CreateTime = DateTime.Now
                    };
                    mesDb.AllocateOrders.Add(curAllocate);
                    mesDb.SaveChanges();
                }

                //var newAllocateProducts = model.Products.Where(_ => !curAllocateProducts.Any(p => StringUtils.Equals(_.ProductCode, p.ProductCode))).ToList();
                //var updateAllocateProducts = model.Products.Where(_ => curAllocateProducts.Any(p => StringUtils.Equals(_.ProductCode, p.ProductCode))).ToList();
                foreach (var newAllocateProduct in model.Products)
                {
                    var productInfo = mesDb.Products.AsNoTracking().FirstOrDefault(_ => _.ProductCode == newAllocateProduct.ProductCode && _.Valid);
                    if (productInfo == null)
                    {
                        logger.Fatal($"搬运单物料不存在，搬运单号：{newAllocateProduct.AllocateNo}，物料：{newAllocateProduct.ProductCode}");
                        continue;
                    }
                    newAllocateProduct.OrderNo = workOrder.OrderNo;
                    newAllocateProduct.WorkOrderNo = workOrder.WorkOrderNo;
                    newAllocateProduct.AllocateNo = model.AllocateNo;
                    newAllocateProduct.ProductName = productInfo.ProductName;
                    newAllocateProduct.Specifications = productInfo.Specifications;
                    newAllocateProduct.CreateTime = DateTime.Now;
                    var category = GetProductMainCategory(productInfo, categories);
                    newAllocateProduct.CategoryCode = category?.CategoryCode;
                    newAllocateProduct.CategoryName = category?.CategoryName;
                    mesDb.AllocateProducts.Add(Mapper.Map<AllocateProduct>(newAllocateProduct));
                }

                //foreach (var product in updateAllocateProducts)
                //{
                //    var entity = curAllocateProducts.FirstOrDefault(_ => StringUtils.Equals(_.ProductCode, product.ProductCode) && StringUtils.Equals(_.BatchNo, product.BatchNo));
                //    if (entity == null)
                //    {
                //        entity = curAllocateProducts.FirstOrDefault(_ => StringUtils.Equals(_.ProductCode, product.ProductCode) && string.IsNullOrWhiteSpace(_.BatchNo));
                //    }

                //    if (entity != null)
                //    {
                //        mesDb.AllocateProducts.Attach(entity);
                //        entity.Quantity = product.Quantity;
                //        entity.BatchNo = product.BatchNo;
                //        entity.ProductionCompany = product.ProductionCompany;
                //        entity.BatchNoBig = product.BatchNoBig;
                //        entity.BatchNoSmall = product.BatchNoSmall;
                //        entity.ModifyBy = 0;
                //        entity.ModifyTime = DateTime.Now;
                //    }
                //    else
                //    {
                //        var productInfo = mesDb.Products.AsNoTracking().FirstOrDefault(_ => _.ProductCode == product.ProductCode && _.Valid);
                //        if (productInfo == null)
                //        {
                //            logger.Fatal($"搬运单物料不存在，搬运单号：{product.AllocateNo}，物料：{product.ProductCode}");
                //            continue;
                //        }
                //        product.AllocateNo = model.AllocateNo;
                //        product.ProductName = productInfo.ProductName;
                //        product.Specifications = productInfo.Specifications;
                //        var category = GetProductMainCategory(productInfo, categories);
                //        product.CategoryCode = category?.CategoryCode;
                //        product.CategoryName = category?.CategoryName;
                //        product.CreateTime = DateTime.Now;
                //        mesDb.AllocateProducts.Add(Mapper.Map<AllocateProduct>(product));
                //    }
                //}
            }
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 获取物料的主分类
        /// </summary>
        /// <param name="product">物料对象</param>
        /// <param name="categories">所有分类</param>
        /// <returns>主分类</returns>
        public static ProductCategory GetProductMainCategory(Product product, List<ProductCategory> categories)
        {
            ProductCategory mainCategory = null;
            var categoryCode = product.CategoryCode;
            while (true)
            {
                if (string.IsNullOrWhiteSpace(categoryCode))
                {
                    break;
                }
                var curCategory = categories.FirstOrDefault(_ => StringUtils.Equals(_.CategoryCode, categoryCode));
                if (curCategory == null)
                {
                    break;
                }
                else if (curCategory.Level == 1)
                {
                    mainCategory = curCategory;
                    break;
                }
                else
                {
                    categoryCode = categories.FirstOrDefault(_ => _.PkId == curCategory.ParentId)?.CategoryCode;
                }
            }

            return mainCategory;
        }

        /// <summary>
        /// 获取调拨物料
        /// </summary>
        /// <param name="pageIndex">页数</param>
        /// <param name="pageSize">条目</param>
        /// <param name="total">总共</param>
        /// <param name="allocateId">调拨单id</param>
        /// <returns></returns>
        public List<VAllocateProduct> QueryAllocateProducts(int pageIndex, int pageSize, out int total, string allocateNo)
        {
            var query = mesDb.AllocateProducts.Where(_ => _.Valid && _.AllocateNo.Contains(allocateNo));
            total = query.Count();
            query = query.OrderBy(_ => _.BatchNo).ThenBy(_ => _.ProductCode);

            var entities = pageSize <= 0 ? query.ToList() : query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            var models = Mapper.Map<List<VAllocateProduct>>(entities);
            return models;
        }

        /// <summary>
        /// 获取调拨单物料
        /// </summary>
        /// <param name="allcateNo">调拨单号</param>
        /// <param name="batchNo">批次号</param>
        /// <returns>调拨单物料</returns>
        public VAllocateProduct GetAllocateProduct(string allcateNo, string batchNo)
        {
            var product = mesDb.AllocateProducts.FirstOrDefault(_ => _.AllocateNo == allcateNo && _.BatchNo == batchNo && _.Valid);
            return Mapper.Map<VAllocateProduct>(product);
        }

        /// <summary>
        /// 获取电池
        /// </summary>
        /// <param name="workOrderNo"></param>
        /// <returns></returns>
        public VAllocateProduct GetEletricProduct(string workOrderNo)
        {
            var allocatrOrderNos = mesDb.AllocateOrders.Where(_ => _.WorkOrderNo == workOrderNo && _.Valid).Select(_ => _.AllocateNo).ToList();
            var allocateProducts = mesDb.AllocateProducts.Where(_ => allocatrOrderNos.Contains(_.AllocateNo) && _.Valid).ToList();
            var productCodes = allocateProducts.Select(_ => _.ProductCode).Distinct().ToList();
            var products = mesDb.Products.Where(_ => productCodes.Contains(_.ProductCode) && new[] { "单晶", "多晶", "PERC单晶", "黑硅" }.Contains(_.CategoryName) && _.Valid).ToList();

            var eletricProducts = allocateProducts.Where(_ => products.Select(p => p.ProductCode).Contains(_.ProductCode)).ToList();

            return Mapper.Map<VAllocateProduct>(eletricProducts.FirstOrDefault());
        }

        /// <summary>
        /// 保存调拨单
        /// </summary>
        /// <param name="model">调拨单对象</param>
        public VAllocateOrder SaveAllocateOrder(VAllocateOrder model)
        {
            var workOrder = mesDb.WorkOrders.AsNoTracking().FirstOrDefault(_ => _.WorkOrderNo == model.WorkOrderNo && _.Valid);
            if (workOrder == null)
            {
                throw new MultiLanBizException(134011);  //工单不存在
            }
            model.OrderNo = workOrder.OrderNo;

            var warehouseCodes = new List<string> { model.FromWarehouseCode, model.ToWarehouseCode };
            var warehouses = mesDb.Warehouses.AsNoTracking().Where(_ => _.Valid && warehouseCodes.Contains(_.WarehouseCode)).ToList();
            model.FromWarehouse = warehouses.FirstOrDefault(_ => StringUtils.Equals(_.WarehouseCode, model.FromWarehouseCode))?.WarehouseName;
            model.ToWarehouse = warehouses.FirstOrDefault(_ => StringUtils.Equals(_.WarehouseCode, model.ToWarehouseCode))?.WarehouseName;

            var entity = mesDb.AllocateOrders.AsNoTracking().FirstOrDefault(_ => _.AllocateNo == model.AllocateNo && _.Valid);
            if (entity == null)
            {
                entity = Mapper.Map<AllocateOrder>(model);
                entity.CreateTime = DateTime.Now;
                entity.ModifyBy = 0;
                mesDb.AllocateOrders.Add(entity);
            }
            else
            {
                mesDb.AllocateOrders.Attach(entity);
                entity.ToWarehouseCode = model.ToWarehouseCode;
                entity.FromWarehouseCode = model.FromWarehouseCode;
                entity.ToWarehouse = model.ToWarehouse;
                entity.FromWarehouse = model.FromWarehouse;
                entity.AllocateTime = model.AllocateTime;
                entity.Remark = model.Remark;
                entity.ModifyBy = model.ModifyBy;
                entity.ModifyTime = DateTime.Now;
            }
            mesDb.SaveChanges();

            return Mapper.Map<VAllocateOrder>(entity);
        }

        /// <summary>
        /// 获取调拨物料
        /// </summary>
        public List<VAllocateProduct> QueryAllocateProductList(string allocateNo)
        {
            var allocate = mesDb.AllocateOrders.AsNoTracking().FirstOrDefault(_ => _.Valid && _.AllocateNo == allocateNo);
            if (allocate == null)
            {
                throw new MultiLanBizException(134007);  //该调拨单不存在，请重新刷新
            }
            var allocateProducts = mesDb.AllocateProducts.AsNoTracking().Where(_ => _.Valid && _.AllocateNo == allocateNo).ToList();
            var result = Mapper.Map<List<VAllocateProduct>>(allocateProducts);

            var boms = mesDb.WorkOrderBoms.AsNoTracking().Where(_ => _.Valid && _.WorkOrderNo == allocate.WorkOrderNo)
                           .Select(_ => new VAllocateProduct
                           {
                               PkId = 0,
                               ProductCode = _.ProductCode,
                               ProductName = _.ProductName,
                               Specifications = _.Specifications,
                               ProductionCompany = _.ProductCompany,
                               Quantity = 0
                           }).ToList();
            result.AddRange(boms.Where(_ => !result.Any(a => StringUtils.Equals(a.ProductCode, _.ProductCode))));

            return result.OrderBy(_ => _.ProductCode).ThenByDescending(_ => _.BatchNo).ThenByDescending(_ => _.Quantity).ToList();
        }

        /// <summary>
        /// 根据工单号和批次号拿产品
        /// </summary>
        /// <param name="workOrderNo">工单号</param>
        /// <param name="batchNo">批次号</param>
        /// <returns>产品对象</returns>
        public VAllocateProduct GetDispatchProduct(string workOrderNo, string batchNo)
        {
            var entity = (from allocate in mesDb.AllocateOrders.AsNoTracking()
                          join product in mesDb.AllocateProducts.AsNoTracking() on allocate.AllocateNo equals product.AllocateNo
                          where allocate.Valid && allocate.WorkOrderNo == workOrderNo && product.Valid && product.BatchNo == batchNo
                          select product).FirstOrDefault();
            return Mapper.Map<VAllocateProduct>(entity);
        }

        /// <summary>
        /// 创建发料单
        /// </summary>
        /// <param name="model">发料单对象</param>
        public void Dispatch(VDispatchOrder model)
        {
            var workOrder = mesDb.WorkOrders.AsNoTracking().FirstOrDefault(_ => _.WorkOrderNo == model.WorkOrderNo && _.Valid);
            if (workOrder == null)
            {
                throw new MultiLanBizException(102002);//$"工单不存在或已被删除，无法修改");
            }
            var batchNos = model.Products.Select(_ => _.BatchNo).Distinct().ToList();
            var products = (from allocate in mesDb.AllocateOrders.AsNoTracking()
                            join product in mesDb.AllocateProducts.AsNoTracking() on allocate.AllocateNo equals product.AllocateNo
                            where allocate.Valid && allocate.WorkOrderNo == model.WorkOrderNo && product.Valid && batchNos.Contains(product.BatchNo)
                            select product).ToList();
            if (batchNos.Any(b => !products.Select(_ => _.BatchNo).Contains(b, StringComparer.CurrentCultureIgnoreCase)))
            {
                throw new MultiLanBizException(133001, string.Join(", ", batchNos.Where(b => !products.Select(_ => _.BatchNo).Contains(b, StringComparer.CurrentCultureIgnoreCase))));//以下批次不存在
            }

            model.OrderNo = workOrder.OrderNo;
            model.WorkshopCode = workOrder.WorkshopCode;
            model.WorkshopName = workOrder.WorkshopName;
            model.CreateTime = DateTime.Now;
            var entity = Mapper.Map<DispatchOrder>(model);
            mesDb.DispatchOrders.Add(entity);
            mesDb.SaveChanges();
            entity.DispatchNo = $"{DateTime.Now:yyMMdd}{entity.PkId:00000}";

            model.Products.ForEach(p =>
            {
                p.ProductCode = products.FirstOrDefault(_ => StringUtils.Equals(p.BatchNo, _.BatchNo)).ProductCode;
                p.ProductName = products.FirstOrDefault(_ => StringUtils.Equals(p.BatchNo, _.BatchNo)).ProductName;
                p.ProductSpecs = products.FirstOrDefault(_ => StringUtils.Equals(p.BatchNo, _.BatchNo)).Specifications;
                p.Color = products.FirstOrDefault(_ => StringUtils.Equals(p.BatchNo, _.BatchNo)).Color;
                p.DispatchNo = entity.DispatchNo;
                p.CreateBy = model.CreateBy;
                p.CreateTime = DateTime.Now;
            });

            mesDb.DispatchOrderProducts.AddRange(Mapper.Map<List<DispatchOrderProduct>>(model.Products));
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 获取发料单对象
        /// </summary>
        /// <param name="dispatchNo">发料单号</param>
        /// <returns>发料单对象</returns>
        public VDispatchOrder GetDispatchOrder(string dispatchNo)
        {
            var entity = mesDb.DispatchOrders.AsNoTracking().FirstOrDefault(_ => _.DispatchNo == dispatchNo && _.Valid);
            if (entity == null)
            {
                return null;
            }
            var products = mesDb.DispatchOrderProducts.AsNoTracking().Where(_ => _.DispatchNo == dispatchNo && _.Valid).ToList();
            var model = Mapper.Map<VDispatchOrder>(entity);
            model.Products = Mapper.Map<List<VDispatchOrderProduct>>(products);

            return model;
        }

        /// <summary>
        /// 查询发料单列表
        /// </summary>
        /// <param name="pageIndex">页面索引</param>
        /// <param name="pageSize">页面容量</param>
        /// <param name="total">全部数量</param>
        /// <param name="dispatchBy">发料人OD</param>
        /// <returns>发料单列表</returns>
        public List<VDispatchOrder> QueryDispatchOrders(int pageIndex, int pageSize, out int total, int dispatchBy)
        {
            var query = mesDb.DispatchOrders.AsNoTracking().Where(_ => _.Valid && _.CreateBy == dispatchBy);
            total = query.Count();
            query = query.OrderByDescending(_ => _.PkId);
            var entities = pageSize <= 0 ? query.ToList() : query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            return Mapper.Map<List<VDispatchOrder>>(entities);
        }

        /// <summary>
        /// 保存调拨单物料
        /// </summary>
        /// <param name="operator">操作人</param>
        /// <param name="allocateNo">调拨单号</param>
        /// <param name="products">物料清单</param>
        public void SaveAllocateProducts(int @operator, string allocateNo, List<VAllocateProduct> products)
        {
            products = products.Where(_ => _.Quantity > 0 && !string.IsNullOrWhiteSpace(_.ProductCode)).ToList();
            var existProducts = products.Where(_ => _.PkId > 0).ToList();
            var newProducts = products.Where(_ => _.PkId <= 0)
                .GroupBy(_ => new { _.ProductCode, _.BatchNo, _.BatchNoBig, _.BatchNoSmall, _.ProductionCompany })
                .Select(_ => new VAllocateProduct
                {
                    ProductCode = _.Key.ProductCode,
                    BatchNo = _.Key.BatchNo,
                    BatchNoBig = _.Key.BatchNoBig,
                    BatchNoSmall = _.Key.BatchNoSmall,
                    ProductionCompany = _.Key.ProductionCompany,
                    Quantity = _.Sum(p => p.Quantity)
                }).ToList();
            foreach (var product in existProducts)
            {
                Predicate<VAllocateProduct> express = _ => StringUtils.Equals(_.ProductCode?.Trim(), product.ProductCode?.Trim())
                    && StringUtils.Equals(_.BatchNo?.Trim(), product.BatchNo?.Trim())
                    && StringUtils.Equals(_.BatchNoBig?.Trim(), product.BatchNoBig?.Trim())
                    && StringUtils.Equals(_.BatchNoSmall?.Trim(), product.BatchNoSmall?.Trim())
                    && StringUtils.Equals(_.ProductionCompany?.Trim(), product.ProductionCompany?.Trim());

                var curNewProducts = newProducts.FindAll(express).ToList();
                product.Quantity = product.Quantity + curNewProducts.Sum(_ => _.Quantity);
                newProducts.RemoveAll(express);
            }

            if (!products.Any())
            {
                return;
            }

            var allocateOrder = mesDb.AllocateOrders.AsNoTracking().FirstOrDefault(_ => _.AllocateNo == allocateNo && _.Valid);
            if (allocateOrder == null)
            {
                throw new MultiLanBizException(134007);  //该调拨单不存在，请重新刷新
            }

            var productCodes = products.Select(_ => _.ProductCode).Distinct().ToList();
            var entities = mesDb.AllocateProducts.AsNoTracking().Where(_ => _.Valid && _.AllocateNo == allocateNo).ToList();
            var productInfos = mesDb.Products.AsNoTracking().Where(_ => _.Valid).ToList();
            var categories = mesDb.ProductCategories.AsNoTracking().Where(_ => _.Valid).ToList();

            foreach (var entity in entities)
            {
                var model = existProducts.FirstOrDefault(_ => _.PkId == entity.PkId);
                if (model == null)
                {
                    mesDb.AllocateProducts.Attach(entity);

                    entity.Valid = false;
                    entity.ModifyBy = @operator;
                    entity.ModifyTime = DateTime.Now;
                }
                else if (!StringUtils.Equals(entity.BatchNo?.Trim(), model.BatchNo?.Trim())
                    || !StringUtils.Equals(entity.BatchNoBig?.Trim(), model.BatchNoBig?.Trim())
                    || !StringUtils.Equals(entity.BatchNoSmall?.Trim(), model.BatchNoSmall?.Trim())
                    || !StringUtils.Equals(entity.ProductionCompany?.Trim(), model.ProductionCompany?.Trim())
                    || entity.Quantity != model.Quantity)
                {
                    mesDb.AllocateProducts.Attach(entity);

                    entity.BatchNo = model.BatchNo;
                    entity.BatchNoBig = model.BatchNoBig;
                    entity.BatchNoSmall = model.BatchNoSmall;
                    entity.ProductionCompany = model.ProductionCompany;
                    entity.Quantity = model.Quantity;
                    entity.ModifyBy = @operator;
                    entity.ModifyTime = DateTime.Now;
                }
            }

            foreach (var model in newProducts)
            {
                var productInfo = productInfos.FirstOrDefault(_ => StringUtils.Equals(_.ProductCode, model.ProductCode));
                if (productInfo == null)
                {
                    continue;
                }
                var category = GetProductMainCategory(productInfo, categories);

                var entity = Mapper.Map<AllocateProduct>(model);
                entity.OrderNo = allocateOrder.OrderNo;
                entity.WorkOrderNo = allocateOrder.WorkOrderNo;
                entity.AllocateNo = allocateNo;
                entity.ProductName = productInfo.ProductName;
                entity.Specifications = productInfo.Specifications;
                entity.CategoryCode = category.CategoryCode;
                entity.CategoryName = category.CategoryName;
                entity.CreateBy = @operator;
                entity.CreateTime = DateTime.Now;
                mesDb.AllocateProducts.Add(entity);
            }
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 查询工单调拨单产品
        /// </summary>
        public List<VAllocateProduct> QueryWorkOrderAllocateProducts(string workOrderNo)
        {
            if (string.IsNullOrWhiteSpace(workOrderNo))
            {
                return new List<VAllocateProduct>();
            }
            var allocateNos = mesDb.AllocateOrders.AsNoTracking().Where(_ => _.WorkOrderNo == workOrderNo && _.Valid).Select(_ => _.AllocateNo).Distinct().ToList();
            var allocateProducts = mesDb.AllocateProducts.AsNoTracking().Where(_ => allocateNos.Contains(_.AllocateNo) && _.Valid).OrderByDescending(_ => _.PkId).ToList();
            return Mapper.Map<List<VAllocateProduct>>(allocateProducts);
        }

        /// <summary>
        /// 审核调拨单
        /// </summary>
        public void ReviewAllocateOrder(int memberId, string allocateNo)
        {
            var entity = mesDb.AllocateOrders.FirstOrDefault(_ => _.Valid && _.AllocateNo == allocateNo);
            if (entity == null)
            {
                throw new MultiLanBizException(134007);  // 该调拨单不存在，请重新刷新
            }

            entity.ReviewBy = memberId;
            entity.ReviewTime = DateTime.Now;
            entity.ModifyBy = memberId;
            entity.ModifyTime = DateTime.Now;
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 查询厂家或规格
        /// </summary>
        /// <param name="categoryName">物料大类</param>
        /// <param name="type">类型</param>
        /// <returns>结果</returns>
        public List<string> QueryFactoriesOrSpecses(string categoryName, int type)
        {
            var query = mesDb.AllocateProducts.AsNoTracking().Where(_ => _.CategoryName == categoryName && _.Valid);
            var result = new List<string>();
            if (type == 0)
            {
                result.AddRange(query.Select(_ => _.ProductionCompany).Distinct().ToList());
            }
            else
            {
                result.AddRange(query.Select(_ => _.Specifications).Distinct().ToList());
            }
            return result.Where(_ => !string.IsNullOrWhiteSpace(_)).OrderBy(_ => _).ToList();
        }

        /// <summary>
        /// 创建调拨单
        /// </summary>
        /// <param name="models">调拨单列表</param>
        public void CreateAllocateOrder(List<VAllocateOrder> models)
        {
            var allocateNos = models.Select(_ => _.AllocateNo.Trim().ToUpper()).Distinct().ToList();
            var workOrderNos = models.Select(_ => _.WorkOrderNo.Trim().ToUpper()).Distinct().ToList();
            var existAllocateOrders = mesDb.AllocateOrders.AsNoTracking().Where(_ => allocateNos.Contains(_.AllocateNo) && _.Valid).ToList();
            var existAllocateProducts = mesDb.AllocateProducts.AsNoTracking().Where(_ => allocateNos.Contains(_.AllocateNo) && _.Valid).ToList();
            var workOrders = mesDb.WorkOrders.AsNoTracking().Where(_ => workOrderNos.Contains(_.WorkOrderNo) && _.Valid).ToList();
            var categories = mesDb.ProductCategories.AsNoTracking().Where(_ => _.Valid).ToList();
            categories.ForEach(_ =>
            {
                //兼容非71类目下的电池片
                if (_.CategoryName.Contains("电池片") && !_.CategoryName.Contains("组件"))
                {
                    _.CategoryName = "电池片";
                }
            });
            var productInfos = mesDb.Products.AsNoTracking().Where(_ => _.Valid).ToList();
            var warehouses = mesDb.Warehouses.AsNoTracking().Where(_ => _.Valid).ToList();

            models.ForEach(model =>
            {
                var workOrder = string.IsNullOrWhiteSpace(model.WorkOrderNo) ? null : workOrders.FirstOrDefault(_ => StringUtils.Equals(_.WorkOrderNo, model.WorkOrderNo));
                var fromWarehouse = string.IsNullOrWhiteSpace(model.FromWarehouseCode) ? null : warehouses.FirstOrDefault(_ => StringUtils.Equals(_.WarehouseCode, model.FromWarehouseCode));
                var toWarehouse = string.IsNullOrWhiteSpace(model.ToWarehouseCode) ? null : warehouses.FirstOrDefault(_ => StringUtils.Equals(_.WarehouseCode, model.ToWarehouseCode));

                model.OrderNo = workOrder?.OrderNo;
                model.FromWarehouseCode = fromWarehouse?.WarehouseCode;
                model.FromWarehouse = fromWarehouse?.WarehouseName;
                model.ToWarehouseCode = toWarehouse?.WarehouseCode;
                model.ToWarehouse = toWarehouse?.WarehouseName;
                model.ReviewBy = 0;
                model.ReviewTime = DateTime.Now;
                model.AllocateTime = DateTime.Now;
                model.CreateTime = DateTime.Now;
                model.Products.ForEach(product =>
                {
                    var productInfo = productInfos.FirstOrDefault(_ => string.Equals(_.ProductCode, product.ProductCode, StringComparison.CurrentCultureIgnoreCase));
                    if (productInfo == null)
                    {
                        logger.Error($"物料编码不存在，调拨单号：{model.AllocateNo}，料号：{product.ProductCode}");
                        return;
                    }
                    product.ProductName = productInfo.ProductName;
                    product.Specifications = productInfo.Specifications;
                    product.CreateTime = DateTime.Now;

                    //判定物料大类
                    var categoryCode = productInfo.CategoryCode;
                    while (true)
                    {
                        if (string.IsNullOrWhiteSpace(categoryCode))
                        {
                            break;
                        }
                        var curCategory = categories.FirstOrDefault(_ => StringUtils.Equals(_.CategoryCode, categoryCode));
                        if (curCategory == null)
                        {
                            break;
                        }
                        else if (curCategory.Level <= 2 || string.Equals(curCategory.CategoryName, "电池片", StringComparison.CurrentCultureIgnoreCase))
                        {
                            product.CategoryCode = curCategory.CategoryCode;
                            product.CategoryName = curCategory.CategoryName;
                            break;
                        }
                        else
                        {
                            categoryCode = categories.FirstOrDefault(_ => _.PkId == curCategory.ParentId)?.CategoryCode;
                        }
                    }
                });
                model.Products = model.Products.Where(_ => !string.IsNullOrWhiteSpace(_.ProductName) && !string.IsNullOrWhiteSpace(_.CategoryCode)).ToList();
            });

            foreach (var model in models)
            {
                var entity = existAllocateOrders.FirstOrDefault(_ => StringUtils.Equals(_.AllocateNo, model.AllocateNo));
                if (entity == null)
                {
                    entity = Mapper.Map<AllocateOrder>(model);
                    mesDb.AllocateOrders.Add(entity);
                }
                else
                {
                    mesDb.AllocateOrders.Attach(entity);
                    entity.FromWarehouseCode = model.FromWarehouseCode;
                    entity.FromWarehouse = model.FromWarehouse;
                    entity.ToWarehouseCode = model.ToWarehouseCode;
                    entity.ToWarehouse = model.ToWarehouse;
                    entity.Remark = model.Remark;
                    entity.ModifyTime = DateTime.Now;
                }
                mesDb.SaveChanges();

                var curAllocateProducts = existAllocateProducts.Where(_ => string.Equals(model.AllocateNo, _.AllocateNo, StringComparison.CurrentCultureIgnoreCase)).ToList();
                var newProducts = model.Products.Where(p => !curAllocateProducts.Select(_ => _.ProductCode).Contains(p.ProductCode, StringComparer.CurrentCultureIgnoreCase)).ToList();
                var removeProducts = curAllocateProducts.Where(p => !model.Products.Select(_ => _.ProductCode).Contains(p.ProductCode, StringComparer.CurrentCultureIgnoreCase) && p.ReceiveQuantity <= 0).ToList();
                var updateProducts = curAllocateProducts.Where(p => model.Products.Select(_ => _.ProductCode).Contains(p.ProductCode, StringComparer.CurrentCultureIgnoreCase)).ToList();

                foreach (var product in newProducts)
                {
                    mesDb.AllocateProducts.Add(Mapper.Map<AllocateProduct>(product));
                    AsyncMainMaterials(model, product);
                }
                removeProducts.ForEach(_ =>
                {
                    mesDb.AllocateProducts.Attach(_);
                    _.Valid = false;
                    _.ModifyTime = DateTime.Now;
                });
                updateProducts.ForEach(_ =>
                {
                    var newProduct = model.Products.FirstOrDefault(p => string.Equals(p.ProductCode, _.ProductCode, StringComparison.CurrentCultureIgnoreCase));
                    mesDb.AllocateProducts.Attach(_);
                    _.ProductCode = newProduct.ProductCode;
                    _.ProductName = newProduct.ProductName;
                    _.Specifications = newProduct.Specifications;
                    _.ProductionCompany = newProduct.ProductionCompany;
                    _.CategoryCode = newProduct.CategoryCode;
                    _.CategoryName = newProduct.CategoryName;
                    _.Quantity = newProduct.Quantity;
                    _.ModifyTime = DateTime.Now;
                });
            }
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 查询工单物料
        /// </summary>
        /// <param name="workOrderNo">工单号</param>
        public List<VAllocateProduct> QueryWorkOrderCells(string workOrderNo)
        {
            var entities = (from a in mesDb.AllocateOrders.AsNoTracking()
                            join ap in mesDb.AllocateProducts.AsNoTracking() on a.AllocateNo equals ap.AllocateNo
                            where a.WorkOrderNo == workOrderNo && a.Valid && ap.CategoryName == "电池片" && ap.Valid
                            select ap).ToList();
            var models = Mapper.Map<List<VAllocateProduct>>(entities.GroupBy(_ => new
            {
                _.ProductCode,
                _.BatchNo,
                _.Color,
                _.Effectiveness,
                _.Power,
                _.ProductionCompany,
                _.Thickness,
                _.Specifications
            }).Select(_ => _.FirstOrDefault()).ToList());

            return models;
        }

        private void AsyncMainMaterials(VAllocateOrder model, VAllocateProduct product)
        {
            try
            {
                var paras = new SqlParameter[] {
                                new SqlParameter("Factory", product.ProductionCompany),
                                new SqlParameter("Specs", product.Specifications),
                                new SqlParameter("WorkOrderNo", model.WorkOrderNo),
                                new SqlParameter("PackStatus", (int)PackStatus.Unpack),
                                new SqlParameter("CabinetStatus", (int)CabinetStatus.UnCabinet)
                        };
                if (string.Equals(product.CategoryName, "电池片", StringComparison.CurrentCultureIgnoreCase))
                {
                    var count = mesDb.Components.AsNoTracking().Count(_ => _.WorkOrderNo == model.WorkOrderNo && _.WeldingTime.HasValue && _.BatterySpecs == "" && _.Valid);
                    if (count > 0)
                    {
                        mesDb.Database.ExecuteSqlCommand("UPDATE Component Set BatteryFactory = @Factory, BatterySpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND WeldingTime IS NOT NULL AND BatterySpecs = '' AND Valid = 1;", paras);
                        mesDb.Database.ExecuteSqlCommand("UPDATE Pack Set BatteryFactory = @Factory, BatterySpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND PackStatus <> @PackStatus AND BatterySpecs = '' AND Valid = 1;", paras);
                        mesDb.Database.ExecuteSqlCommand("UPDATE Cabinet Set BatteryFactory = @Factory, BatterySpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND Status <> @CabinetStatus AND BatterySpecs = '' AND Valid = 1;", paras);
                    }
                }
                else if (string.Equals(product.CategoryName, "背板", StringComparison.CurrentCultureIgnoreCase))
                {
                    var count = mesDb.Components.AsNoTracking().Count(_ => _.WorkOrderNo == model.WorkOrderNo && _.LaminatedTime.HasValue && _.BackPlanSpecs == "" && _.Valid);
                    if (count > 0)
                    {
                        mesDb.Database.ExecuteSqlCommand("UPDATE Component Set BackPlanFactory = @Factory, BackPlanSpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND LaminatedTime IS NOT NULL AND BackPlanSpecs = '' AND Valid = 1;", paras);
                        mesDb.Database.ExecuteSqlCommand("UPDATE Pack Set BackPlanFactory = @Factory, BackPlanSpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND PackStatus <> @PackStatus AND BackPlanSpecs = '' AND Valid = 1;", paras);
                        mesDb.Database.ExecuteSqlCommand("UPDATE Cabinet Set BackPlanFactory = @Factory, BackPlanSpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND Status <> @CabinetStatus AND BackPlanSpecs = '' AND Valid = 1;", paras);
                    }
                }
                else if (string.Equals(product.CategoryName, "接线盒", StringComparison.CurrentCultureIgnoreCase))
                {
                    var count = mesDb.Components.AsNoTracking().Count(_ => _.WorkOrderNo == model.WorkOrderNo && _.FramingTime.HasValue && _.JunBoxSpecs == "" && _.Valid);
                    if (count > 0)
                    {
                        mesDb.Database.ExecuteSqlCommand("UPDATE Component Set JunBoxFactory = @Factory, JunBoxSpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND FramingTime IS NOT NULL AND JunBoxSpecs = '' AND Valid = 1;", paras);
                        mesDb.Database.ExecuteSqlCommand("UPDATE Pack Set JunBoxFactory = @Factory, JunBoxSpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND PackStatus <> @PackStatus AND JunBoxSpecs = '' AND Valid = 1;", paras);
                        mesDb.Database.ExecuteSqlCommand("UPDATE Cabinet Set JunBoxFactory = @Factory, JunBoxSpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND Status <> @CabinetStatus AND JunBoxSpecs = '' AND Valid = 1;", paras);
                    }
                }
                else if (string.Equals(product.CategoryName, "边框", StringComparison.CurrentCultureIgnoreCase))
                {
                    var count = mesDb.Components.AsNoTracking().Count(_ => _.WorkOrderNo == model.WorkOrderNo && _.FramingTime.HasValue && _.AluFrameSpecs == "" && _.Valid);
                    if (count > 0)
                    {
                        mesDb.Database.ExecuteSqlCommand("UPDATE Component Set AluFrameFactory = @Factory, AluFrameSpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND FramingTime IS NOT NULL AND AluFrameSpecs = '' AND Valid = 1;", paras);
                        mesDb.Database.ExecuteSqlCommand("UPDATE Pack Set AluFrameFactory = @Factory, AluFrameSpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND PackStatus <> @PackStatus AND AluFrameSpecs = '' AND Valid = 1;", paras);
                        mesDb.Database.ExecuteSqlCommand("UPDATE Cabinet Set AluFrameFactory = @Factory, AluFrameSpecs = @Specs WHERE WorkOrderNo = @WorkOrderNo AND Status <> @CabinetStatus AND AluFrameSpecs = '' AND Valid = 1;", paras);
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Fatal(ex, $"调拨单更新主材规格失败，工单号：{model.WorkOrderNo}，调拨单号：{model.AllocateNo}，产品代码：{product.ProductCode}，产品名称：{product.ProductName}，错误信息：{ex.Message}");
            }
        }

        /// <summary>
        /// 从ESB同步批次号到MES
        /// </summary>
        /// <param name="allocateNo">调拨单号</param>
        /// <param name="allocateProducts">产品清单</param>
        /// <param name="userId">操作人</param>
        public void AsyncBatchNos(string allocateNo, List<VAllocateProduct> allocateProducts, int userId)
        {
            var allocateOrder = mesDb.AllocateOrders.AsNoTracking().FirstOrDefault(_ => _.AllocateNo == allocateNo && _.Valid);
            if (allocateOrder == null)
            {
                throw new BizException(1, $"调拨单号不存在：{allocateNo}");
            }

            var products = mesDb.AllocateProducts.AsNoTracking().Where(_ => _.AllocateNo == allocateNo && _.Valid).ToList();
            foreach (var item in products)
            {
                var model = allocateProducts.FirstOrDefault(_ => string.Equals(_.ProductCode, item.ProductCode, StringComparison.CurrentCultureIgnoreCase));
                if (model != null && !string.Equals(model.BatchNo, item.BatchNo, StringComparison.CurrentCultureIgnoreCase))
                {
                    mesDb.AllocateProducts.Attach(item);
                    if (!string.IsNullOrWhiteSpace(item.BatchNo))
                    {
                        logger.Warn($"调拨单物料批次号变更，单号：{allocateNo}，物料编号：{item.ProductCode}，老批次号：{item.BatchNo}，新批次号：{model.BatchNo}");
                    }
                    item.BatchNo = model.BatchNo;
                    item.ModifyBy = userId;
                    item.ModifyTime = DateTime.Now;
                }
            }

            mesDb.AllocateOrders.Attach(allocateOrder);
            allocateOrder.BatchLastAsyncBy = userId;
            allocateOrder.BatchLastAsyncTime = DateTime.Now;
            allocateOrder.BatchAsyncFinished = !products.Any(_ => string.IsNullOrWhiteSpace(_.BatchNo));
            allocateOrder.ModifyBy = userId;
            allocateOrder.ModifyTime = DateTime.Now;

            mesDb.SaveChanges();
        }
    }
}